package scuts.core;

import haxe.FastList;
import scuts.core.types.Tup2;
import scuts.core.types.Tup3;


using scuts.core.extensions.Ints;

interface Functor < A > {
  function fmap <B>(a:A, f:A->B):Functor<B>;
}


class ZipArray {
	public static function zipWith < A, B, C > (arr1:Array<A>, arr2:Array<B>, f:A->B->C):Array<C>
	{
		var min = arr1.length.min(arr2.length);
		var res = [];
		for (i in 0...min) {
			res.push(f(arr1[i], arr2[i]));
		}
		return res;
	}
  
  public static function zipWith2 < A, B, C, D > (arr1:Array<A>, arr2:Array<B>, arr3:Array<C>, f:A->B->C->D):Array<D>
	{
		var min = arr1.length.min(arr2.length).min(arr3.length);
		var res = [];
		for (i in 0...min) {
			res.push(f(arr1[i], arr2[i], arr3[i]));
		}
		return res;
	}
	
	public static function zip < A, B, C > (arr1:Array<A>, arr2:Array<B>):Array<Tup2<A,B>>
	{
		var min = arr1.length.min(arr2.length);
		var res = [];
		for (i in 0...min) {
			res.push(Tup2.create(arr1[i], arr2[i]));
		}
    
		return res;
	}
}

class ZipIterable {
	public static function zipWith < A, B, C > (arr1:Iterable<A>, arr2:Iterable<B>, f:A->B->C):Iterable<C>
	{
    var it1 = arr1.iterator(), it2 = arr2.iterator();
    var res = [];
		while (it1.hasNext() && it2.hasNext()) {
      res.push(f(it1.next(), it2.next()));
    }
		
		return res;
	}
  
  public static function zipWith2 < A, B, C, D > (arr1:Iterable<A>, arr2:Iterable<B>, arr3:Iterable<C>, f:A->B->C->D):Iterable<D>
	{
    var it1 = arr1.iterator(), it2 = arr2.iterator(), it3 = arr3.iterator();
    var res = [];
		while (it1.hasNext() && it2.hasNext() && it3.hasNext()) {
      res.push(f(it1.next(), it2.next(), it3.next()));
    }
		return res;
	}
	
	public static function zip < A, B, C > (arr1:Iterable<A>, arr2:Iterable<B>):Iterable<Tup2<A,B>>
	{
    var it1 = arr1.iterator(), it2 = arr2.iterator();
		var res = [];
		while (it1.hasNext() && it2.hasNext()) {
      res.push(Tup2.create(it1.next(), it2.next()));
    }
		return res;
	}
  
  public static function zip2 < A, B, C, D > (arr1:Iterable<A>, arr2:Iterable<B>, arr3:Iterable<C>):Iterable<Tup3<A,B,C>>
	{
    var it1 = arr1.iterator(), it2 = arr2.iterator(), it3 = arr3.iterator();
		var res = [];
		while (it1.hasNext() && it2.hasNext() && it3.hasNext()) {
      res.push(Tup3.create(it1.next(), it2.next(), it3.next()));
    }
    
		return res;
	}
}